package com.ff.sca.common.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/** * MD5工具类，提供字符串MD5加密（校验）、文件MD5值获取（校验）功能。 */ 
public class MD5Util { /** * 16进制字符集 */ 
	private static final char HEX_DIGITS[] = {
		'0', '1', '2', '3', '4', '5', '6', '7', 
		'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; /** * 指定算法为MD5的MessageDigest */ 
	private static MessageDigest messageDigest = null; /** * 初始化messageDigest的加密算法为MD5 */ 
	static {
		try { 
			messageDigest = MessageDigest.getInstance("MD5");
		} 
		catch(NoSuchAlgorithmException e) 
		{ 
			e.printStackTrace(); 
		}
	} 

	public static byte[] byteMerger(byte[] byte_1, byte[] byte_2){   
		byte[] byte_3 = new byte[byte_1.length+byte_2.length];   
		System.arraycopy(byte_1, 0, byte_3, 0, byte_1.length);   
		System.arraycopy(byte_2, 0, byte_3, byte_1.length, byte_2.length);   
		return byte_3;   
	} 

	/** * 获取文件的MD5值 
	 * @param file 目标文件 * @return MD5字符串 
	 * @throws IOException */ 
	public static String getFileMD5String(File file) throws IOException 
	{ 
		String ret = ""; 
		synchronized (ret) {
			FileInputStream in = null; 
			FileChannel ch = null; 
			try { 
				in = new FileInputStream(file); 
				ch = in.getChannel(); 
				long q = file.length();
				ByteBuffer byteBuffer;
				byte[] byteBufferb;
				if(q>1024*1024)
				{
					int strLength = (int)Math.floor((1024*1024)/3);
					byte[] a = new byte[strLength];
					byte[] b = new byte[strLength+1];
					byte[] c = new byte[strLength];
					ch.map(FileChannel.MapMode.READ_ONLY, 0, strLength).get(a);
					ch.map(FileChannel.MapMode.READ_ONLY, (long)Math.floor(q/2), strLength+1).get(b);
					ch.map(FileChannel.MapMode.READ_ONLY, q-strLength, strLength).get(c);
					byteBufferb = byteMerger(byteMerger(a,b),c);
				}
				else
				{
					byteBuffer = ch.map(FileChannel.MapMode.READ_ONLY, 0, q);
					byteBufferb = new byte[Integer.parseInt(String.valueOf(q))]; 
					byteBuffer.get(byteBufferb);
				}
	
				//			in.read(byteBuffer,  Long.bitCount(q)-2025, 1024);
				messageDigest.update(byteBufferb); 
				ret = bytesToHex(messageDigest.digest());
			} 
			finally { 
				if(in != null) 
				{ 
					try { in.close(); 
					} 
					catch(IOException e) 
					{ 
						e.printStackTrace(); 
					} 
				} 
				if(ch != null) 
				{ 
					try { ch.close();
					} 
					catch(IOException e)
					{ e.printStackTrace();
					}
				}
			} 
		}
		return ret;
	}
	/** * 获取文件的MD5值 *
	 *  @param fileName 目标文件的完整名称 * @return MD5字符串 
	 * @throws IOException */ 
	public static String getFileMD5String(String fileName) throws IOException 
	{ 
		return getFileMD5String(new File(fileName)); 
	}
	/** * MD5加密字符串 * @param str 目标字符串 * @return MD5加密后的字符串 */ 
	public static String getMD5String(String str) 
	{ 
		return getMD5String(str.getBytes()); 
	}
	/** * MD5加密以byte数组表示的字符串 * @param bytes 目标byte数组 * @return MD5加密后的字符串 */ 
	public static String getMD5String(byte[] bytes) 
	{ 
		messageDigest.update(bytes);
		return bytesToHex(messageDigest.digest()); 
	} 
	/** * 校验密码与其MD5是否一致 * @param pwd 密码字符串 * @param md5 基准MD5值 * @return 检验结果 */ 
	public static boolean checkPassword(String pwd, String md5) 
	{ 
		return getMD5String(pwd).equalsIgnoreCase(md5); 
	} 
	/** * 校验密码与其MD5是否一致 * @param pwd 以字符数组表示的密码 * @param md5 基准MD5值 * @return 检验结果 */ 
	public static boolean checkPassword(char[] pwd, String md5) 
	{ 
		return checkPassword(new String(pwd), md5); 
	}
	/** * 检验文件的MD5值 * @param file 目标文件 * @param md5 基准MD5值 * @return 检验结果 
	 * @throws IOException */ 
	public static boolean checkFileMD5(File file, String md5) throws IOException 
	{ 
		return getFileMD5String(file).equalsIgnoreCase(md5); 
	} 
	/** * 检验文件的MD5值 * @param fileName 目标文件的完整名称 * @param md5 基准MD5值 * @return 检验结果 
	 * @throws IOException */ 
	public static boolean checkFileMD5(String fileName, String md5) throws IOException 
	{ 
		return checkFileMD5(new File(fileName), md5); 
	}
	/** * 将字节数组转换成16进制字符串 * @param bytes 目标字节数组 * @return 转换结果 */ 
	public static String bytesToHex(byte bytes[]) 
	{ 
		return bytesToHex(bytes, 0, bytes.length); 
	} 
	/** * 将字节数组中指定区间的子数组转换成16进制字符串
	 *   @param bytes 目标字节数组 
	 *   
	 *  @param start 起始位置（包括该位置） 
	 *  * @param end 结束位置（不包括该位置）
	 *  * @return 转换结果 */
	public static String bytesToHex(byte bytes[], int start, int end)
	{ 
		String sb = ""; 
		for(int i = start; i < start + end; i++) 
		{
			sb=sb+byteToHex(bytes[i]); 
		} return sb; 
	} 
	/** * 将单个字节码转换成16进制字符串 * @param bt 目标字节 * @return 转换结果 */ 
	public static String byteToHex(byte bt) 
	{
		return HEX_DIGITS[(bt & 0xf0) >> 4] + "" + HEX_DIGITS[bt & 0xf];
	} 
	public static void main(String[] args) throws IOException 
	{ 
		long begin = System.currentTimeMillis();
		String md5 = getFileMD5String(new File("E:/test/专利检索(1).rar")); 
		long end = System.currentTimeMillis();
		if(md5.equals("5d74b725d63fad6a94aac7f6d4ed0533"))
			System.out.println("ok!");
		System.out.println("MD5:\t" + md5 + "\nTime:\t" + (end - begin) + "ms"); 
	} 
}